package com.tianji.promotion.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.enums.CouponStatus;
import com.tianji.promotion.domain.enums.ExchangeCodeStatus;
import com.tianji.promotion.domain.enums.ObtainType;
import com.tianji.promotion.domain.enums.UserCouponStatus;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.UserCouponQuery;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.UserCouponMapper;
import com.tianji.promotion.service.ICouponService;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.utils.RedisLock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-03-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {

    private final CouponMapper couponMapper;

    private final IExchangeCodeService ecService;

    private final StringRedisTemplate redisTemplate;
    private final RedissonClient redissonClient;

    /**
     * 用户端展示优惠券列表
     *
     * @return
     */
    @Override
    public List<CouponVO> listVO() {
        QueryWrapper<Coupon> queryWrapper = new QueryWrapper<Coupon>().eq("obtain_way", ObtainType.PUBLIC)
                .eq("status", CouponStatus.ISSUING);

        List<Coupon> list = couponMapper.selectList(queryWrapper);

        List<CouponVO> res = new ArrayList<>();
        // 查询userCoupon表
        Map<Long, UserCoupon> userMp = this.lambdaQuery()
                .eq(UserCoupon::getUserId, UserContext.getUser())
                .list().stream().collect(Collectors.toMap(v -> v.getCouponId(), v -> v));

        // 是否可以领取
        LocalDateTime now = LocalDateTime.now();
        for (Coupon coupon : list) {
            CouponVO couponVO = BeanUtils.copyBean(coupon, CouponVO.class);

            if (now.isAfter(coupon.getIssueBeginTime()) || now.isBefore(coupon.getIssueEndTime())) {
                couponVO.setAvailable(true);
            }

            UserCoupon userCoupon = userMp.get(coupon.getId());
            // 是否可以使用
            if (userCoupon != null) {
                if (userCoupon.getTermBeginTime() != null && userCoupon.getTermEndTime() != null) {
                    if (now.isAfter(userCoupon.getTermBeginTime()) && now.isBefore(userCoupon.getTermEndTime())) {
                        couponVO.setReceived(true);
                    }
                }
            }
            res.add(couponVO);
        }
        return res;

    }

    /**
     * 用户通过下单方式领取优惠券
     *
     * @param id
     */
    @Override
    public void receiveCoupon(Long id) {
        // 进行参数校验
        LocalDateTime now = LocalDateTime.now();
        Coupon coupon = couponMapper.selectById(id);
        if (coupon == null) {
            throw new IllegalArgumentException("优惠券不存在");
        }
        if (coupon.getStatus() != CouponStatus.ISSUING) {
            throw new IllegalArgumentException("优惠券状态不正确");
        }
        if (coupon.getIssueNum() >= coupon.getTotalNum()) {
            throw new IllegalArgumentException("优惠券已发放完毕");
        }
        if (coupon.getIssueBeginTime() != null && coupon.getIssueEndTime() != null) {
            if (now.isBefore(coupon.getIssueBeginTime()) || now.isAfter(coupon.getIssueEndTime())) {
                throw new IllegalArgumentException("优惠券不在领取时间范围内");
            }
        }
        // 填充用户优惠券信息
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setStatus(UserCouponStatus.UNUSED);
        userCoupon.setUserId(UserContext.getUser());
        userCoupon.setCouponId(id);
        if (coupon.getTermBeginTime() != null && coupon.getTermEndTime() != null) {
            userCoupon.setTermBeginTime(coupon.getTermBeginTime());
            userCoupon.setTermEndTime(coupon.getTermEndTime());
        }
        if (coupon.getTermDays() != null) {
            userCoupon.setTermBeginTime(now);
            userCoupon.setTermEndTime(now.plusDays(coupon.getTermDays()));
        }
        // 保存用户优惠券
        this.save(userCoupon);
        // 优惠券发放数量+1
        couponMapper.updateIssueNum(id);
    }

    /**
     * 分页查询优惠券
     *
     * @param ucQuery
     * @return
     */
    @Override
    public PageDTO<UserCoupon> pageQuery(UserCouponQuery ucQuery) {
        Page<UserCoupon> page =this.lambdaQuery().eq(UserCoupon::getUserId, UserContext.getUser())
                .eq(ucQuery.getStatus()!= null, UserCoupon::getStatus, ucQuery.getStatus())
                .page(ucQuery.toMpPageDefaultSortByCreateTimeDesc());

        List<UserCoupon> records = page.getRecords();
        if (ObjectUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        return PageDTO.of(page, records);
    }

    /**
     * 通过兑换码兑换优惠券
     *
     * @param ecode
     */
    @Override
    @Transactional
    public void exchangeByCode(String ecode) {

        // 校验兑换码
        ExchangeCode exCode = ecService.lambdaQuery().eq(ExchangeCode::getCode, ecode).one();
        if (exCode == null) {
            throw new IllegalArgumentException("兑换码不存在");
        }
        Long couponId = exCode.getExchangeTargetId();
        if (exCode.getStatus() != ExchangeCodeStatus.UNUSED) {
            throw new IllegalArgumentException("兑换码状态不正确");
        }

        // 校验优惠券信息
        Coupon coupon = checkCoupon(couponId);

        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setStatus(UserCouponStatus.UNUSED);
        userCoupon.setUserId(UserContext.getUser());
        userCoupon.setCouponId(couponId);
        if (coupon.getTermBeginTime() != null && coupon.getTermEndTime() != null) {
            userCoupon.setTermBeginTime(coupon.getTermBeginTime());
            userCoupon.setTermEndTime(coupon.getTermEndTime());
        }
        if (coupon.getTermDays() != null) {
            userCoupon.setTermBeginTime(LocalDateTime.now());
            userCoupon.setTermEndTime(LocalDateTime.now().plusDays(coupon.getTermDays()));
        }
        // 校验用户券信息
        // 判断用户是否有资格领取这张券
//        synchronized (UserContext.getUser().toString().intern()) {
//            checkUser(couponId, coupon);
//            // 保存用户券
//            this.save(userCoupon);
//        }
        // 换成trylock的方式
        RedisLock redisLock = new RedisLock("lock:coupon:userid" + UserContext.getUser(), redisTemplate);
        if (redisLock.tryLock(10L, TimeUnit.SECONDS)) {
            try {
                checkUser(couponId, coupon);
                // 保存用户券
                this.save(userCoupon);
                int r = couponMapper.updateIssueNum(couponId);
                if (r == 0) {
                    throw new BizIllegalException("??");
                }
            } finally {
                redisLock.unlock();
            }
        }

        // 更新兑换码信息
        ecService.lambdaUpdate().eq(ExchangeCode::getCode, ecode)
                .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
                .update();

        // 更新优惠券发放数量，让他加一，在高并发场景下可能会超出限制，也就是超卖
        // UpdateWrapper<Coupon> updateWrapper = new UpdateWrapper<>();
        // updateWrapper.eq("id", couponId).setSql("issue_num = issue_num + 1");
        // couponMapper.update(null, updateWrapper);
        // 先查询出来原来的发放数量
        // Integer num = couponMapper.selectById(couponId).getIssueNum();
        // 再做乐观锁比较

    }

    private void checkUser(Long couponId, Coupon coupon) {

        Integer count = this.lambdaQuery()
                .eq(UserCoupon::getUserId, UserContext.getUser())
                .eq(UserCoupon::getCouponId, couponId)
                .count();
        if (count != null && count >= coupon.getUserLimit()) {
            throw new IllegalArgumentException("用户领取该优惠券的数量已经到达了最大值，不能再兑换了");
        }
    }

    private Coupon checkCoupon(Long couponId) {
        Coupon coupon = couponMapper.selectById(couponId);
        if (coupon == null) {
            throw new IllegalArgumentException("优惠券不存在");
        }
        if (coupon.getStatus() != CouponStatus.ISSUING) {
            throw new IllegalArgumentException("优惠券状态不正确");
        }
        if (coupon.getIssueNum() >= coupon.getTotalNum()) {
            throw new IllegalArgumentException("优惠券已发放完毕");
        }
        if (coupon.getIssueBeginTime() != null && coupon.getIssueEndTime() != null) {
            if (LocalDateTime.now().isBefore(coupon.getIssueBeginTime())
                    || LocalDateTime.now().isAfter(coupon.getIssueEndTime())) {
                throw new IllegalArgumentException("优惠券不在领取时间范围内");
            }
        }
        return coupon;
    }
}
